using System;
using System.Collections.Generic;

namespace lab02
{
    internal interface IMatrix
    {
        void SetValue(int i, int j, int value);
        int GetValue(int i, int j);
        int GetNumRows();
        int GetNumColumns();
    }

    internal interface IDrawableMatrix : IMatrix
    {
        void Draw(IMatrixDrawer drawer);
    }

    internal abstract class DrawableMatrix : IDrawableMatrix
    {
        protected List<IVector> MatrixColumns;

        #region IDrawableMatrix Members

        public virtual void SetValue(int i, int j, int value)
        {
            MatrixColumns[i].SetValue(j, value);
        }

        public virtual int GetValue(int i, int j)
        {
            return MatrixColumns[i].GetValue(j);
        }

        public virtual int GetNumRows()
        {
            return MatrixColumns[0].GetNumRows();
        }

        public virtual int GetNumColumns()
        {
            return MatrixColumns.Count;
        }

        public void Draw(IMatrixDrawer drawer)
        {
            InternalDrawer(drawer, this);
        }

        #endregion

        public abstract void InternalDrawer(IMatrixDrawer drawer, DrawableMatrix matrix);
    }

    internal class DenseMatrix : DrawableMatrix
    {
        public DenseMatrix(int width, int height)
        {
            MatrixColumns = new List<IVector>(width);
            for (int i = 0; i < width; i++)
            {
                MatrixColumns.Add(new DenseVector(height));
            }
        }

        public override void InternalDrawer(IMatrixDrawer drawer, DrawableMatrix matrix)
        {
            drawer.BorderDrawer(matrix.GetNumColumns(), matrix.GetNumRows());
            for (int i = 0; i < matrix.GetNumColumns(); i++)
            {
                for (int j = 0; j < matrix.GetNumRows(); j++)
                {
                    drawer.CellDrawer(i, j);
                    drawer.ValueDrawer(i, j, matrix.GetValue(i, j));
                }
            }
        }
    }

    internal class SparseMatrix : DrawableMatrix
    {
        public SparseMatrix(int width, int height)
        {
            MatrixColumns = new List<IVector>(width);
            for (int i = 0; i < width; i++)
            {
                MatrixColumns.Add(new SparseVector(height));
            }
        }

        public override void InternalDrawer(IMatrixDrawer drawer, DrawableMatrix matrix)
        {
            drawer.BorderDrawer(matrix.GetNumColumns(), matrix.GetNumRows());
            for (int i = 0; i < matrix.GetNumColumns(); i++)
                for (int j = 0; j < matrix.GetNumRows(); j++)
                {
                    drawer.CellDrawer(i, j);
                    int value = matrix.GetValue(i, j);
                    if (value != 0)
                        drawer.ValueDrawer(i, j, value);
                }
        }
    }

    internal class MatrixRoutines
    {
        public static void Fill(IMatrix matrix, int numNonZeros, int maxValue)
        {
            Random rnd = new Random();
            for (int i = 0; i < numNonZeros; i++)
            {
                int value = rnd.Next(1, maxValue);
                bool isAdded = false;
                while (!isAdded)
                {
                    int x = rnd.Next(0, matrix.GetNumColumns());
                    int y = rnd.Next(0, matrix.GetNumRows());
                    if (matrix.GetValue(x, y) != 0) continue;
                    matrix.SetValue(x, y, value);
                    isAdded = true;
                }
            }
        }

        public static void Print(IMatrix matrix)
        {
            for (int y = 0; y < matrix.GetNumRows(); y++)
            {
                for (int x = 0; x < matrix.GetNumColumns(); x++)
                {
                    Console.Write(@"{0,4}", matrix.GetValue(x, y));
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }

    internal class MatrixStatistics
    {
        private readonly IMatrix _matrix;

        public MatrixStatistics(IMatrix m)
        {
            _matrix = m;
        }

        public int Sum()
        {
            int sum = 0;
            for (int x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (int y = 0; y < _matrix.GetNumRows(); y++)
                {
                    sum += _matrix.GetValue(x, y);
                }
            }
            return sum;
        }

        public int Nonz()
        {
            int nonz = 0;
            for (int x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (int y = 0; y < _matrix.GetNumRows(); y++)
                {
                    if (_matrix.GetValue(x, y) != 0) nonz++;
                }
            }
            return nonz;
        }

        public int Max()
        {
            int max = 0;
            for (int x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (int y = 0; y < _matrix.GetNumRows(); y++)
                {
                    int cur = _matrix.GetValue(x, y);
                    if (cur > max) max = cur;
                }
            }
            return max;
        }

        public double Middle()
        {
            return 1.0*Sum()/Nonz();
        }
    }
}